﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Web;
using Microsoft.Extensions.Caching.Distributed;
using TK.Common.Tools;

namespace TK.Common.Authorization
{
    /// <summary>
    /// SimpleAuthHandler
    /// </summary>
    public class SimpleAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        private readonly IDistributedCache _cache;
        private readonly IConfiguration _configuration;

        /// <summary>
        /// 加密key
        /// </summary>
        public const string AES_KEY = "handday.scrm.key";

        /// <summary>
        /// SchemeName
        /// </summary>
        public const string SchemeName = "SimpleAuth";

        /// <summary>
        /// ctor
        /// </summary>
        public SimpleAuthHandler(
            IOptionsMonitor<AuthenticationSchemeOptions> options,
            IConfiguration configuration,
            ILoggerFactory loggerFactory,
            UrlEncoder encoder,
            IDistributedCache cache,
            ISystemClock clock)
            : base(options, loggerFactory, encoder, clock)
        {
            _cache = cache;
            _configuration = configuration;
        }

        /// <summary>
        /// HandleAuthenticateAsync
        /// </summary>
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            var key = "scrmtoken";
            var key1 = "Authorization";
            var encryptInfo = Request.Headers[key];
            if (string.IsNullOrWhiteSpace(encryptInfo))
            {
                encryptInfo = Request.Query[key];
            }

            if (string.IsNullOrWhiteSpace(encryptInfo))
            {
                encryptInfo = Request.Headers["Authorization"];
            }

            if (string.IsNullOrWhiteSpace(encryptInfo))
            {
                return AuthenticateResult.Fail($"Missing Authorization Header_{Request.Headers[key]}_{Request.Query[key]}_{Request.Headers[key1]}");
            }

            encryptInfo = HttpUtility.UrlDecode(encryptInfo, Encoding.UTF8);

            try
            {
                //解密信息
                var decryptInfo = AESCryptography.Decrypt(encryptInfo, "handday.scrm.key");
                if (string.IsNullOrWhiteSpace(decryptInfo))
                {
                    return AuthenticateResult.Fail("decrypt faild");
                }

                //UserInfoAddDto
                //数据格式：userid|username|corpid|thirdcorpuserid|ismanage|sourcetype|corpname
                var claimArray = decryptInfo.Split('|');

                //缓存操作
                var cacheKey = $"{claimArray[2]}_{claimArray[0]}";
                if (string.IsNullOrWhiteSpace(await _cache.GetStringAsync(cacheKey)))
                {
                    return AuthenticateResult.Fail("token expired");
                }

                var expireHour = int.Parse(_configuration["Token.Expire.Hour"]);
                await _cache.SetStringAsync(cacheKey, encryptInfo, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = new System.TimeSpan(expireHour, 0, 0) });

                //生成票据
                var ticket = BuildTicket(claimArray);
                await Task.CompletedTask;

                return AuthenticateResult.Success(ticket);
            }
            catch (Exception e)
            {
                return AuthenticateResult.Fail($"user data error_{e.Message}");
            }
        }

        #region private methods

        private AuthenticationTicket BuildTicket(string[] claimArray)
        {
            var claims = new[]
                {
                    new Claim("userid", claimArray[0]),
                    new Claim("username", claimArray[1]),
                    new Claim("corpid",claimArray[2]),
                    new Claim("thirdcorpuserid",claimArray[3]),
                    new Claim("ismanage",claimArray[4]),
                    new Claim("sourcetype",claimArray[5]),
                    new Claim("corpname",claimArray[6]),
                };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);

            return ticket;
        }

        #endregion private methods
    }
}